﻿// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Windows.Forms;

/// <summary>
///  Represents a control of a task dialog.
/// </summary>
public abstract class TaskDialogControl
{
    // Disallow inheritance by specifying a private protected constructor.
    private protected TaskDialogControl()
    {
    }

    /// <summary>
    ///  Gets or sets the object that contains data about the control.
    /// </summary>
    /// <value>
    ///  An <see cref="object"/> that contains data about the control.
    ///  The default is <see langword="null"/>.
    /// </value>
    public object? Tag { get; set; }

    /// <summary>
    ///  Gets the <see cref="TaskDialogPage"/> instance which this control
    ///  is currently bound to.
    /// </summary>
    /// <value>
    ///  The <see cref="TaskDialogPage"/> instance which this control is bound to, or
    ///  <see langword="null"/> if this control is not currently bound.
    /// </value>
    /// <remarks>
    ///  <para>
    ///   A control will be bound to a page while it is being displayed, for exactly the
    ///   same time as the returned page is bound to a dialog that can be retrieved by
    ///   <see cref="TaskDialogPage.BoundDialog"/>.
    ///  </para>
    ///  <para>
    ///   While a control is bound to a page, you cannot show that control instance using a
    ///   different <see cref="TaskDialogPage"/> instance at the same time.
    ///  </para>
    /// </remarks>
    public TaskDialogPage? BoundPage { get; private set; }

    /// <summary>
    ///  Gets a value that indicates whether the current state of this control
    ///  allows it to be created in a task dialog when binding it.
    /// </summary>
    internal virtual bool IsCreatable => true;

    /// <summary>
    ///  Gets or sets a value that indicates whether this control has been created
    ///  in a bound task dialog.
    /// </summary>
    internal bool IsCreated { get; private set; }

    internal TASKDIALOG_FLAGS Bind(TaskDialogPage page)
    {
        BoundPage = page.OrThrowIfNull();

        // Use the current value of IsCreatable to determine if the control is
        // created. This is important because IsCreatable can change while the
        // control is displayed (e.g. if it depends on the Text property).
        IsCreated = IsCreatable;

        return IsCreated ? BindCore() : default;
    }

    internal void Unbind()
    {
        if (IsCreated)
        {
            UnbindCore();
        }

        IsCreated = false;
        BoundPage = null;
    }

    /// <summary>
    ///  Applies initialization after the task dialog is displayed or navigated.
    /// </summary>
    internal void ApplyInitialization()
    {
        // Only apply the initialization if the control is actually created.
        if (IsCreated)
        {
            ApplyInitializationCore();
        }
    }

    /// <summary>
    ///  When overridden in a derived class, runs additional binding logic and returns
    ///  flags to be specified before the task dialog is displayed or navigated.
    /// </summary>
    /// <remarks>
    ///  <para>
    ///   This method will only be called if <see cref="IsCreatable"/> returns <see langword="true"/>.
    ///  </para>
    /// </remarks>
    /// <returns></returns>
    private protected virtual TASKDIALOG_FLAGS BindCore() => default;

    /// <summary>
    ///
    /// </summary>
    /// <remarks>
    ///  <para>
    ///   This method will only be called if <see cref="BindCore"/> was called.
    ///  </para>
    /// </remarks>
    private protected virtual void UnbindCore()
    {
    }

    /// <summary>
    ///  When overridden in a subclass, applies initialization after the task dialog
    ///  is displayed or navigated.
    /// </summary>
    /// <remarks>
    ///  <para>
    ///   This method will only be called if <see cref="IsCreatable"/> returns <see langword="true"/>.
    ///  </para>
    /// </remarks>
    private protected virtual void ApplyInitializationCore()
    {
    }

    private protected void DenyIfBound() => BoundPage?.DenyIfBound();

    private protected void DenyIfWaitingForInitialization() => BoundPage?.DenyIfWaitingForInitialization();

    private protected void DenyIfNotBoundOrWaitingForInitialization()
    {
        DenyIfWaitingForInitialization();

        if (BoundPage is null)
        {
            throw new InvalidOperationException(SR.TaskDialogControlNotBound);
        }
    }

    private protected void DenyIfBoundAndNotCreated()
    {
        if (BoundPage is not null && !IsCreated)
        {
            throw new InvalidOperationException(SR.TaskDialogControlNotCreated);
        }
    }
}
